package com.example.yhmap_outline;

import org.apache.http.HttpStatus;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.*;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;

/**
 * 2022/8/23
 *
 * @author fengyu
 */
@Component
public class Download {
    private static final Logger log = LoggerFactory.getLogger(Download.class);
    @Resource
    HttpPooling httpPooling;

    @Value("${yh.wmtsurl}")
    private String wmtsurl = "http://10.32.185.77:8083/wmtsproxy/wmts/yhemap";

    @Value("${yh.templatequery}")
    private String queryPath = "http://10.32.185.77:8083/wmtsproxy/wmts/rest/{layer}/{style}/{TileMatrixSet}/{TileMatrix}/{TileRow}/{TileCol}?format=image/png";

    @Value("${yh.pngdir}")
    private String pngDir = "d:/yhemap";

    private String host;
    private int port;
    private String layerName;

    private final DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yy-MM-dd HH:mm:ss");
    /**
     * 地球半径
     */
    private final double dqbj = 6378137d;
    /**
     * 1英寸96个像素点
     */
    private final int dpi = 96;
    /**
     * 1英寸对应米
     */
    private final double unit = 0.025428516728689246;

    /**
     * 每度多少米
     */
    private final double mmd = 2 * Math.PI * dqbj / 360;

    /**
     * 第0级，最大分辨率，360度除以256像素
     */
    private static final double maxResolution = 360d / 256;

    // 分辨率 = 比例尺 * （unit/dpi）

    /**
     * 瓦片的初始层级，通常是从0开始到20级，以数字表示，获取到瓦片切图分辨率后可以通过计算得出余杭的初始切图层级11
     */
    private int initLevel = 0;

    @Value("${pool.size}")
    private int poolSize = 10;
    private final ScheduledExecutorService service = Executors.newScheduledThreadPool(poolSize);

    @PostConstruct
    public void init() {
        log.info("{} 开始获取 {}" , LocalDateTime.now().format(dateTimeFormatter), wmtsurl);
        String strs[] = wmtsurl.replace("http://", "").replace("https://", "").split("/");
        if (strs.length < 2){
            log.info("wmts服务地址错误");
            return;
        }

        String str2[] = strs[0].split(":");
        host = str2[0];
        port = (str2.length > 1 && str2[1].matches("\\d+")) ? Integer.parseInt(str2[1]) : 80;

        layerName = strs[strs.length -1];
        String style = "default";
        queryPath = queryPath.replace("{layer}", layerName).replace("{style}", style);

        Optional.ofNullable(getTileMatrixSet()).ifPresent(wmtsTileMatrixSet -> {
            log.info("获取到瓦片分层：{} 共 {} 级",wmtsTileMatrixSet.getId(), wmtsTileMatrixSet.getMatrixSet().size());
            log.info("瓦片总数 {}", wmtsTileMatrixSet.getTileCount());
            queryPath = queryPath.replace("{TileMatrixSet}", wmtsTileMatrixSet.getId());
            download(wmtsTileMatrixSet);
        });
    }

    /**
     * 从wmtsurl服务获取瓦片分层信息
     */
    private WmtsTileMatrixSet getTileMatrixSet(){
        Document document = null;
        try (CloseableHttpResponse response = httpPooling.getHttpClient(host, port).execute(new HttpGet(wmtsurl + "?request=GetCapabilities"))){
            if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK){
                SAXReader saxReader = new SAXReader();
                document = saxReader.read(response.getEntity().getContent());
                response.getEntity().writeTo(new FileOutputStream(pngDir + "/info.xml"));
            }
        } catch (IOException | DocumentException e) {
            e.printStackTrace();
            return null;
        }
        WmtsTileMatrixSet matrixSet = new WmtsTileMatrixSet();
        Optional.ofNullable(document).ifPresent(doc -> {
            Element rootElement = doc.getRootElement();
            Element contents = rootElement.element("Contents");
            List<Element> lys = contents.elements("Layer");
            Element matrixSetLink = lys.stream().filter(lyer -> layerName.equalsIgnoreCase(lyer.element("Identifier").getText()))
                    .findAny().map(ly ->{
                        Optional.ofNullable(ly.attribute("date")).ifPresent(date ->{
                            log.info("该地图服务{}的发布时间：{}",layerName, date.getText());
                            // TODO 根据时间判断是否需要下载地图
                        });
                        return ly.element("TileMatrixSetLink");
                    }).orElse(null);
            Optional.ofNullable(matrixSetLink).ifPresent(link -> {
                String id = link.element("TileMatrixSet").getText();
                matrixSet.setId(id);
                List<Element> limits = link.element("TileMatrixSetLimits").elements("TileMatrixLimits");
                limits.forEach(e -> matrixSet.getMatrixSet().add(new WmtsTileMatrix(
                        e.elementText("TileMatrix"),
                        Integer.parseInt(e.elementText("MinTileRow")),
                        Integer.parseInt(e.elementText("MaxTileRow")),
                        Integer.parseInt(e.elementText("MinTileCol")),
                        Integer.parseInt(e.elementText("MaxTileCol"))
                )));
            });

            // 取比例尺计算分辨率
            contents.elements("TileMatrixSet").stream()
                    .filter(s -> matrixSet.getId().equalsIgnoreCase(s.elementText("Identifier")))
                    .findAny().ifPresent(s -> s.elements("TileMatrix")
                            .forEach(t -> {
                                String scale = t.elementText("ScaleDenominator");
                                double resolution = Double.parseDouble(scale) / mmd * (unit/dpi);
                                matrixSet.addResolution(resolution);
                            })
                    );
            // 根据分辨率计算初始层级
            double max = matrixSet.getFirstResolutions();
            initLevel = (int) (Math.log(maxResolution/max)/Math.log(2));
        });

        return matrixSet;
    }

    private void download(WmtsTileMatrixSet wmtsTileMatrixSet){
        for (WmtsTileMatrix wmtsTileMatrix : wmtsTileMatrixSet.getMatrixSet()) {
            accept(wmtsTileMatrix);
        }
        service.shutdown();
        while (true){
            if (service.isTerminated()) break;
        }
        try {
            FileWriter fileWriter = new FileWriter(pngDir + "/info.txt");
            fileWriter.write("resolutions: " + wmtsTileMatrixSet.getResolutions());
            fileWriter.flush();
            fileWriter.close();
        } catch (IOException e) {
            log.info("resolutions: {}", wmtsTileMatrixSet.getResolutions());
        }
        log.info("执行完毕");
    }

    private synchronized String createQueryPath(final String z, final int row, final int col){
        return  queryPath.replace("{TileMatrix}", z)
                        .replace("{TileRow}", String.valueOf(row))
                        .replace("{TileCol}", String.valueOf(col));
    }

    private Runnable getPng(final String url, final String filePath){
        return () -> {
            try (CloseableHttpResponse response = httpPooling.getHttpClient(host, port).execute(new HttpGet(url))){
                if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
                    InputStream inputStream =  response.getEntity().getContent();
                    BufferedImage imageIO = ImageIO.read(inputStream);
                    ImageIO.write(imageIO, "png", new File(filePath));
                    inputStream.close();
                } else {
                    log.info("获取失败：{}", url);
                }
            } catch (IOException e) {
                e.printStackTrace();
                log.info("获取失败：{}", url);
            }
        };
    }

    private void accept(WmtsTileMatrix matrix) {
        String tileMarrixSet = matrix.getId();
        String strs[] = tileMarrixSet.split(":");
        String level = strs[strs.length - 1];
        if (level.matches("\\d+")) {
            level = String.valueOf(Integer.parseInt(level) + initLevel);
        }
        for (int i = matrix.getMinRow(); i <= matrix.getMaxRow(); i++) {
            File dir = new File(pngDir + "/" + level + "/" + i);
            if (!dir.isDirectory()) {
                if (!dir.mkdirs()){
                    log.error("创建目录不成功：{},跳过本行瓦片", dir.getPath());
                    continue;
                }
            }
            for (int j = matrix.getMinCol(); j <= matrix.getMaxCol(); j++) {
                service.execute(getPng(
                        createQueryPath(tileMarrixSet, i, j),
                        dir.getPath() + "/" + j + ".png"));
            }
        }
    }
}
